/*
 * Project CHEN Rendering Engine : /shader.cpp
 * Created: 2020-12-16, Last modified:  2021-03-03
 * 
 * This is the default descriptor.
 * 
 * Copyright (C) 2021 Hineven (hineven@pku.edu.cn)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "shader.hpp"

namespace chen {

unsigned __sampler_shader_pixel_count = 0;

void SamplerShader::rthread (int tid) const {
    SampleHolder * sh = new SampleHolder(camera);
    int w, h;
    Sampler * sampler = sampler_f->clone(tid);
    while(fetchNext(&w, &h)) {
        for(int i = 0; i<min(SamplerShaderStatics::BLOCK_SIZE, camera->film->resolution_w-w); i++) 
            for(int j = 0; j<min(SamplerShaderStatics::BLOCK_SIZE, camera->film->resolution_h-h); j++) {
                // enumerate every single pixel
                __sampler_shader_pixel_count ++;
                Float pdf;
                Point2*u = sampler->gen2DArray(samples_per_pixel);
                for(int c = 0; c<samples_per_pixel; c++) {
                    Point2 p(w+i+u[c].x, h+j+u[c].y);
                    Ray r = camera->rayFromPixel(p.x, p.y, &pdf);
                    sh->push(Sample(p, Li(r, sampler)/pdf));
                }
                delete u;
            }
    }
    sh->finish();
    delete sh;

    LOG(INFO) << "render thread quit";
}

bool SamplerShader::fetchNext (int * w, int * h) const {
    pthread_mutex_lock(&flock);
    *w = lw, *h = lh;
    if(lh >= camera->film->resolution_h) {
        pthread_mutex_unlock(&flock);
        return false;
    }
    lw += SamplerShaderStatics::BLOCK_SIZE;
    if(lw >= camera->film->resolution_w)
        lw = 0, lh += SamplerShaderStatics::BLOCK_SIZE;
    pthread_mutex_unlock(&flock);
    return true;
}

void SamplerShader::prepare(int ntr) 
{
    Shader::prepare(ntr);
    LOG(INFO) << "[Sampler Shader] noting to prepare";
}

void SamplerShader::render () {
    LOG(INFO) << "[Sampler Shader] render begin";
    if(nthreads != 1) {
        std::thread * tr[8];
        for(int i = 0; i<max(8, nthreads); i++) tr[i] = new std::thread(&SamplerShader::rthread, this, i);
        for(int i = 0; i<max(8, nthreads); i++) tr[i]->join();
    } else rthread(0);
    return ;
}


// TODO: No importance sampling the bsdf yet.
Spectrum Shader::estimateDirectIlluminationND (
    const SurfInteract & si, int nsamples, Sampler * sampler
) const {
    if(si.bsdf->count(FBSDF_DELTA) != si.bsdf->ncomponents) {
        Spectrum ret = 0;
        for(int i = 0; i<nsamples; i++) {
            // Random pick a area light respect to power distribution
            // for better estimation.
            Float lpdf;
            const Light* pl;
            while(true) {
                pl = this->scene->pickLightRespectToPower(sampler->gen(), &lpdf);
                if(lpdf != 0) break;
            }
            
            Vector3 wi;
            bool occluded;
            Float pdf;
            // The light source supports sampling light rays
            if(hasFlag(pl->flags, FLIGHT_SAMPLE_LI)) {
                Point2 u = sampler->gen2D();
                // Sample a light ray carrying RADIANCE, to estimate the integral in
                // the LTE equation.
                Spectrum lit = ((AreaLight*)pl)->sampleLi(si, u, &wi, &pdf, &occluded);
                if(pdf == 0) continue ;
                if(!occluded) { // 如果没有被碰撞阻隔
                    ret += 
                        lit
                    * si.bsdf->f(wi, -si.wh)
                    * (abs(si.ns.weigh(wi)) / (pdf*lpdf));
                }
            } else {
                // Calculate the integral directly (cos the L function of the light source
                // is probably a delta function)
                Spectrum lit = pl->Li(si, &wi, &occluded);
                if(!occluded) {
                    ret += 
                        lit
                    * si.bsdf->f(wi, -si.wh)
                    * (abs(dot((Vector3)si.ns, wi)) / lpdf);
                }
            }
        }
        return ret/nsamples;
    } else {
        return 0;
    }
}

}
